package hcn.util.validation;

import lombok.extern.slf4j.Slf4j;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.regex.Pattern;

/**
 * @Author: zhusm@bsoft.com.cn
 *
 * @Description: 字段验证工具
 *
 * @Create: 2019-01-24 11:05
 *
 * @Version 1.0
 **/
@Slf4j
public class ValidationUtil {
    public static <T> void validation(T t) throws AnnotaionException {
        Class<?> clazz = t.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field: fields) {
            getAnnotaionValue(field,t);
        }
    }

    private static <T> void getAnnotaionValue(Field field, T t) throws AnnotaionException {
        ValidationAnnotaion declaredAnnotation = field.getDeclaredAnnotation(ValidationAnnotaion.class);
        ValidationAnnotaion.Null nullAnnotaion = field.getDeclaredAnnotation(ValidationAnnotaion.Null.class);
        ValidationAnnotaion.NotNull notNullAnnotaion = field.getDeclaredAnnotation(ValidationAnnotaion.NotNull.class);
        ValidationAnnotaion.FiledExpression filedExpAnnotaion = field.getDeclaredAnnotation(ValidationAnnotaion.FiledExpression.class);
        try {
            PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(), t.getClass());
            Method readMethod = propertyDescriptor.getReadMethod();
            Object invoke = readMethod.invoke(t);
            if (null != declaredAnnotation){
                //普通校验
                dealwithAnnotaion(invoke,field,declaredAnnotation);
            }
            if (null != nullAnnotaion){
                //空值校验
                dealwithAnnotaionNull(invoke,field,nullAnnotaion);
            }
            if (null != notNullAnnotaion){
                //非空校验
                dealwithAnnotaionNotNull(invoke,field,notNullAnnotaion);
            }
            if (null != filedExpAnnotaion){
                //正则表达式校验
                dealwithAnnotaionFiledExp(invoke,field,filedExpAnnotaion);
            }
        } catch (IntrospectionException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    private static void dealwithAnnotaionFiledExp(Object invoke,Field field, ValidationAnnotaion.FiledExpression filedExpAnnotaion) throws AnnotaionException {
        if (StringUtil.isEmpty(filedExpAnnotaion.filedExpression())){
            throw new AnnotaionException("Expression cannot be empty");
        }
        boolean matches = Pattern.matches(filedExpAnnotaion.filedExpression(), (String) invoke);
        if (!matches){
            log.error(filedExpAnnotaion.filedExpression());
            throw new AnnotaionException(field.getName()+" 格式错误");
        }

    }

    private static void dealwithAnnotaionNotNull(Object invoke,Field field, ValidationAnnotaion.NotNull declaredAnnotation) throws AnnotaionException {
        if (invoke == null && StringUtil.isEmpty(declaredAnnotation.errorDescription())){
            throw new AnnotaionException(field.getName()+" is not null");
        }
        if (invoke == null && !StringUtil.isEmpty(declaredAnnotation.errorDescription())){
            throw new AnnotaionException(declaredAnnotation.errorDescription());
        }
        switch (declaredAnnotation.type()){
            case STRING:
                if (String.valueOf(invoke).length() == 0 && StringUtil.isEmpty(declaredAnnotation.errorDescription())){
                    throw new AnnotaionException(field.getName()+" length is 0");
                }else if (String.valueOf(invoke).length() == 0 && !StringUtil.isEmpty(declaredAnnotation.errorDescription())){
                    throw new AnnotaionException(declaredAnnotation.errorDescription());
                }
                break;
            case NUMBER:
                break;
            default:
                break;
        }

    }

    private static void dealwithAnnotaionNull(Object invoke,Field field,ValidationAnnotaion.Null nullAnnotaion) throws AnnotaionException {
        if (invoke != null){
            if (StringUtil.isEmpty(nullAnnotaion.errorDescription())){
                throw new AnnotaionException(field.getName()+" must be null");
            }else {
                throw new AnnotaionException(nullAnnotaion.errorDescription());
            }
        }
        if (invoke instanceof String && String.valueOf(invoke).length() !=0){
            if (StringUtil.isEmpty(nullAnnotaion.errorDescription())){
                throw new AnnotaionException(field.getName()+" must be null");
            }else {
                throw new AnnotaionException(nullAnnotaion.errorDescription());
            }
        }
    }

    private static void dealwithAnnotaion(Object invoke,Field field, ValidationAnnotaion declaredAnnotation) throws AnnotaionException {
        if (invoke == null) return;
        switch (declaredAnnotation.type()){
            case STRING:
                stringTypeDealwith((String) invoke,field,declaredAnnotation);
                break;
            case NUMBER:
                numberTypeDealwith((Number) invoke,field,declaredAnnotation);
                break;
            default:
                break;
        }
    }

    private static void numberTypeDealwith(Number invoke, Field field, ValidationAnnotaion declaredAnnotation) throws AnnotaionException {
        if (invoke instanceof Integer){
            int value = invoke.intValue();
            if (value < declaredAnnotation.minValue() || value > declaredAnnotation.maxValue()){
                if (StringUtil.isEmpty(declaredAnnotation.errorDescription())){
                    throw new AnnotaionException(field.getName()+" not in range");
                }else {
                    throw new AnnotaionException(declaredAnnotation.errorDescription());
                }
            }
        }
        if (invoke instanceof Long){
            long value = invoke.longValue();
            if (value < declaredAnnotation.minValue() || value > declaredAnnotation.maxValue()){
                if (StringUtil.isEmpty(declaredAnnotation.errorDescription())){
                    throw new AnnotaionException(field.getName()+" not in range");
                }else {
                    throw new AnnotaionException(declaredAnnotation.errorDescription());
                }
            }
        }
        if (invoke instanceof Float){
            float value = invoke.floatValue();
            if (value < declaredAnnotation.minValue() || value > declaredAnnotation.maxValue()){
                if (StringUtil.isEmpty(declaredAnnotation.errorDescription())){
                    throw new AnnotaionException(field.getName()+" not in range");
                }else {
                    throw new AnnotaionException(declaredAnnotation.errorDescription());
                }
            }
        }
        if (invoke instanceof Double){
            double value = invoke.doubleValue();
            if (value < declaredAnnotation.minValue() || value > declaredAnnotation.maxValue()){
                if (StringUtil.isEmpty(declaredAnnotation.errorDescription())){
                    throw new AnnotaionException(field.getName()+" not in range");
                }else {
                    throw new AnnotaionException(declaredAnnotation.errorDescription());
                }
            }
        }
        if (invoke instanceof Byte){
            byte value = invoke.byteValue();
            if (value < declaredAnnotation.minValue() || value > declaredAnnotation.maxValue()){
                if (StringUtil.isEmpty(declaredAnnotation.errorDescription())){
                    throw new AnnotaionException(field.getName()+" not in range");
                }else {
                    throw new AnnotaionException(declaredAnnotation.errorDescription());
                }
            }
        }
        if (invoke instanceof Short){
            short value = invoke.shortValue();
            if (value < declaredAnnotation.minValue() || value > declaredAnnotation.maxValue()){
                if (StringUtil.isEmpty(declaredAnnotation.errorDescription())){
                    throw new AnnotaionException(field.getName()+" not in range");
                }else {
                    throw new AnnotaionException(declaredAnnotation.errorDescription());
                }
            }
        }
    }

    private static void stringTypeDealwith(String invoke,Field field,ValidationAnnotaion validationAnnotaion) throws AnnotaionException {
        int length = String.valueOf(invoke).length();
        if (length < validationAnnotaion.minLength() || length > validationAnnotaion.maxLength()){
            if (StringUtil.isEmpty(validationAnnotaion.errorDescription())){
                throw new AnnotaionException(field.getName()+" not in range");
            }else {
                throw new AnnotaionException(validationAnnotaion.errorDescription());
            }
        }
    }

    public static void main(String[] args) throws AnnotaionException {
        TestFiled testFiled = new TestFiled();
        testFiled.setAddress("安徽省五芜湖");
        testFiled.setEge(120);
        testFiled.setName("xiaomming");
        testFiled.setEmail("13260929989");
        ValidationUtil.validation(testFiled);
    }
}
